// Emacs style mode select	 -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// $Log:$
//
// DESCRIPTION:
//		Archiving: SaveGame I/O.
//
//-----------------------------------------------------------------------------

static const char
rcsid[] = "$Id: p_tick.c,v 1.4 1997/02/03 16:47:55 b1 Exp $";

#include "i_system.h"
#include "z_zone.h"
#include "p_local.h"

// State.
#include "doomstat.h"
#include "r_state.h"

byte*			save_p;


// Pads save_p to a 4-byte boundary
//	so that the load/save works on SGI&Gecko.
#define PADSAVEP()		save_p += (4 - ((int) save_p & 3)) & 3



//
// P_ArchivePlayers
//
void P_ArchivePlayers (void)
{
    int			i;
    int			j;
    player_t*	dest;

    for (i=0 ; i<MAXPLAYERS ; i++)
    {
        if (!playeringame[i])
            continue;

        PADSAVEP();

        dest = (player_t *)save_p;
        memcpy (dest,&players[i],sizeof(player_t));
        save_p += sizeof(player_t);
        for (j=0 ; j<NUMPSPRITES ; j++)
        {
            if (dest->psprites[j].state)
            {
                dest->psprites[j].state
                    = (state_t *)(dest->psprites[j].state-states);
            }
        }
    }
}



//
// P_UnArchivePlayers
//
void P_UnArchivePlayers (void)
{
    int			i;
    int			j;

    for (i=0 ; i<MAXPLAYERS ; i++)
    {
        if (!playeringame[i])
            continue;

        PADSAVEP();

        memcpy (&players[i],save_p, sizeof(player_t));
        save_p += sizeof(player_t);

        // will be set when unarc thinker
        players[i].mo = NULL;
        players[i].message = NULL;
        players[i].attacker = NULL;

        for (j=0 ; j<NUMPSPRITES ; j++)
        {
            if (players[i]. psprites[j].state)
            {
                players[i]. psprites[j].state
                    = &states[ (int)players[i].psprites[j].state ];
            }
        }
    }
}


//
// P_ArchiveWorld
//
void P_ArchiveWorld (void)
{
    int					i;
    int					j;
    sector_t*			sec;
    line_t*				li;
    side_t*				si;
    short*				put;

    put = (short *)save_p;

    // do sectors
    for (i=0, sec = sectors ; i<numsectors ; i++,sec++)
    {
        *put++ = sec->floorheight >> FRACBITS;
        *put++ = sec->ceilingheight >> FRACBITS;
        *put++ = sec->floorpic;
        *put++ = sec->ceilingpic;
        *put++ = sec->lightlevel;
        *put++ = sec->special;			// needed?
        *put++ = sec->tag;				// needed?
    }


    // do lines
    for (i=0, li = lines ; i<numlines ; i++,li++)
    {
        *put++ = li->flags;
        *put++ = li->special;
        *put++ = li->tag;
        for (j=0 ; j<2 ; j++)
        {
            if (li->sidenum[j] == -1)
                continue;

            si = &sides[li->sidenum[j]];

            *put++ = si->textureoffset >> FRACBITS;
            *put++ = si->rowoffset >> FRACBITS;
            *put++ = si->toptexture;
            *put++ = si->bottomtexture;
            *put++ = si->midtexture;
        }
    }

    save_p = (byte *)put;
}



//
// P_UnArchiveWorld
//
void P_UnArchiveWorld (void)
{
    int					i;
    int					j;
    sector_t*			sec;
    line_t*				li;
    side_t*				si;
    short*				get;

    get = (short *)save_p;

    // do sectors
    for (i=0, sec = sectors ; i<numsectors ; i++,sec++)
    {
        sec->floorheight = *get++ << FRACBITS;
        sec->ceilingheight = *get++ << FRACBITS;
        sec->floorpic = *get++;
        sec->ceilingpic = *get++;
        sec->lightlevel = *get++;
        sec->special = *get++;			// needed?
        sec->tag = *get++;				// needed?
        sec->specialdata = 0;
        sec->soundtarget = 0;
    }

    // do lines
    for (i=0, li = lines ; i<numlines ; i++,li++)
    {
        li->flags = *get++;
        li->special = *get++;
        li->tag = *get++;
        for (j=0 ; j<2 ; j++)
        {
            if (li->sidenum[j] == -1)
                continue;
            si = &sides[li->sidenum[j]];
            si->textureoffset = *get++ << FRACBITS;
            si->rowoffset = *get++ << FRACBITS;
            si->toptexture = *get++;
            si->bottomtexture = *get++;
            si->midtexture = *get++;
        }
    }
    save_p = (byte *)get;
}





//
// Thinkers
//
typedef enum
{
    tc_end,
    tc_mobj

} thinkerclass_t;



//
// P_ArchiveThinkers
//
void P_ArchiveThinkers (void)
{
    thinker_t*			th;
    mobj_t*				mobj;

    // save off the current thinkers
    for (th = thinkercap.next ; th != &thinkercap ; th=th->next)
    {
        if (th->function.acp1 == (actionf_p1)P_MobjThinker)
        {
            *save_p++ = tc_mobj;
            PADSAVEP();
            mobj = (mobj_t *)save_p;
            memcpy (mobj, th, sizeof(*mobj));
            save_p += sizeof(*mobj);
            mobj->state = (state_t *)(mobj->state - states);

            if (mobj->player)
                mobj->player = (player_t *)((mobj->player-players) + 1);
            continue;
        }

        // I_Error ("P_ArchiveThinkers: Unknown thinker function");
    }

    // add a terminating marker
    *save_p++ = tc_end;
}



//
// P_UnArchiveThinkers
//
void P_UnArchiveThinkers (void)
{
    byte				tclass;
    thinker_t*			currentthinker;
    thinker_t*			next;
    mobj_t*				mobj;

    // remove all the current thinkers
    currentthinker = thinkercap.next;
    while (currentthinker != &thinkercap)
    {
        next = currentthinker->next;

        if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker)
            P_RemoveMobj ((mobj_t *)currentthinker);
        else
            Z_Free (currentthinker);

        currentthinker = next;
    }
    P_InitThinkers ();

    // read in saved thinkers
    while (1)
    {
        tclass = *save_p++;
        switch (tclass)
        {
            case tc_end:
                return;		// end of list

            case tc_mobj:
                PADSAVEP();
                mobj = Z_Malloc (sizeof(*mobj), PU_LEVEL, NULL);
                memcpy (mobj, save_p, sizeof(*mobj));
                save_p += sizeof(*mobj);
                mobj->state = &states[(int)mobj->state];
                mobj->target = NULL;
                if (mobj->player)
                {
                    mobj->player = &players[(int)mobj->player-1];
                    mobj->player->mo = mobj;
                }
                P_SetThingPosition (mobj);
                mobj->info = &mobjinfo[mobj->type];
                mobj->floorz = mobj->subsector->sector->floorheight;
                mobj->ceilingz = mobj->subsector->sector->ceilingheight;
                mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker;
                P_AddThinker (&mobj->thinker);
                break;

            default:
                I_Error ("Unknown tclass %i in savegame",tclass);
        }

    }

}


//
// P_ArchiveSpecials
//
enum
{
    tc_ceiling,
    tc_door,
    tc_floor,
    tc_plat,
    tc_flash,
    tc_strobe,
    tc_glow,
    tc_endspecials

} specials_e;



//
// Things to handle:
//
// T_MoveCeiling, (ceiling_t: sector_t * swizzle), - active list
// T_VerticalDoor, (vldoor_t: sector_t * swizzle),
// T_MoveFloor, (floormove_t: sector_t * swizzle),
// T_LightFlash, (lightflash_t: sector_t * swizzle),
// T_StrobeFlash, (strobe_t: sector_t *),
// T_Glow, (glow_t: sector_t *),
// T_PlatRaise, (plat_t: sector_t *), - active list
//
void P_ArchiveSpecials (void)
{
    thinker_t*			th;
    ceiling_t*			ceiling;
    vldoor_t*			door;
    floormove_t*		floor;
    plat_t*				plat;
    lightflash_t*		flash;
    strobe_t*			strobe;
    glow_t*				glow;
    int					i;

    // save off the current thinkers
    for (th = thinkercap.next ; th != &thinkercap ; th=th->next)
    {
        if (th->function.acv == (actionf_v)NULL)
        {
            for (i = 0; i < MAXCEILINGS; i++)
                if (activeceilings[i] == (ceiling_t *)th)
                    break;

            if (i<MAXCEILINGS)
            {
                *save_p++ = tc_ceiling;
                PADSAVEP();
                ceiling = (ceiling_t *)save_p;
                memcpy (ceiling, th, sizeof(*ceiling));
                save_p += sizeof(*ceiling);
                ceiling->sector = (sector_t *)(ceiling->sector - sectors);
            }
            continue;
        }

        if (th->function.acp1 == (actionf_p1)T_MoveCeiling)
        {
            *save_p++ = tc_ceiling;
            PADSAVEP();
            ceiling = (ceiling_t *)save_p;
            memcpy (ceiling, th, sizeof(*ceiling));
            save_p += sizeof(*ceiling);
            ceiling->sector = (sector_t *)(ceiling->sector - sectors);
            continue;
        }

        if (th->function.acp1 == (actionf_p1)T_VerticalDoor)
        {
            *save_p++ = tc_door;
            PADSAVEP();
            door = (vldoor_t *)save_p;
            memcpy (door, th, sizeof(*door));
            save_p += sizeof(*door);
            door->sector = (sector_t *)(door->sector - sectors);
            continue;
        }

        if (th->function.acp1 == (actionf_p1)T_MoveFloor)
        {
            *save_p++ = tc_floor;
            PADSAVEP();
            floor = (floormove_t *)save_p;
            memcpy (floor, th, sizeof(*floor));
            save_p += sizeof(*floor);
            floor->sector = (sector_t *)(floor->sector - sectors);
            continue;
        }

        if (th->function.acp1 == (actionf_p1)T_PlatRaise)
        {
            *save_p++ = tc_plat;
            PADSAVEP();
            plat = (plat_t *)save_p;
            memcpy (plat, th, sizeof(*plat));
            save_p += sizeof(*plat);
            plat->sector = (sector_t *)(plat->sector - sectors);
            continue;
        }

        if (th->function.acp1 == (actionf_p1)T_LightFlash)
        {
            *save_p++ = tc_flash;
            PADSAVEP();
            flash = (lightflash_t *)save_p;
            memcpy (flash, th, sizeof(*flash));
            save_p += sizeof(*flash);
            flash->sector = (sector_t *)(flash->sector - sectors);
            continue;
        }

        if (th->function.acp1 == (actionf_p1)T_StrobeFlash)
        {
            *save_p++ = tc_strobe;
            PADSAVEP();
            strobe = (strobe_t *)save_p;
            memcpy (strobe, th, sizeof(*strobe));
            save_p += sizeof(*strobe);
            strobe->sector = (sector_t *)(strobe->sector - sectors);
            continue;
        }

        if (th->function.acp1 == (actionf_p1)T_Glow)
        {
            *save_p++ = tc_glow;
            PADSAVEP();
            glow = (glow_t *)save_p;
            memcpy (glow, th, sizeof(*glow));
            save_p += sizeof(*glow);
            glow->sector = (sector_t *)(glow->sector - sectors);
            continue;
        }
    }

    // add a terminating marker
    *save_p++ = tc_endspecials;

}


//
// P_UnArchiveSpecials
//
void P_UnArchiveSpecials (void)
{
    byte				tclass;
    ceiling_t*			ceiling;
    vldoor_t*			door;
    floormove_t*		floor;
    plat_t*				plat;
    lightflash_t*		flash;
    strobe_t*			strobe;
    glow_t*				glow;


    // read in saved thinkers
    while (1)
    {
        tclass = *save_p++;
        switch (tclass)
        {
            case tc_endspecials:
                return;		// end of list

            case tc_ceiling:
                PADSAVEP();
                ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVEL, NULL);
                memcpy (ceiling, save_p, sizeof(*ceiling));
                save_p += sizeof(*ceiling);
                ceiling->sector = &sectors[(int)ceiling->sector];
                ceiling->sector->specialdata = ceiling;

                if (ceiling->thinker.function.acp1)
                    ceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling;

                P_AddThinker (&ceiling->thinker);
                P_AddActiveCeiling(ceiling);
                break;

            case tc_door:
                PADSAVEP();
                door = Z_Malloc (sizeof(*door), PU_LEVEL, NULL);
                memcpy (door, save_p, sizeof(*door));
                save_p += sizeof(*door);
                door->sector = &sectors[(int)door->sector];
                door->sector->specialdata = door;
                door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor;
                P_AddThinker (&door->thinker);
                break;

            case tc_floor:
                PADSAVEP();
                floor = Z_Malloc (sizeof(*floor), PU_LEVEL, NULL);
                memcpy (floor, save_p, sizeof(*floor));
                save_p += sizeof(*floor);
                floor->sector = &sectors[(int)floor->sector];
                floor->sector->specialdata = floor;
                floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor;
                P_AddThinker (&floor->thinker);
                break;

            case tc_plat:
                PADSAVEP();
                plat = Z_Malloc (sizeof(*plat), PU_LEVEL, NULL);
                memcpy (plat, save_p, sizeof(*plat));
                save_p += sizeof(*plat);
                plat->sector = &sectors[(int)plat->sector];
                plat->sector->specialdata = plat;

                if (plat->thinker.function.acp1)
                    plat->thinker.function.acp1 = (actionf_p1)T_PlatRaise;

                P_AddThinker (&plat->thinker);
                P_AddActivePlat(plat);
                break;

            case tc_flash:
                PADSAVEP();
                flash = Z_Malloc (sizeof(*flash), PU_LEVEL, NULL);
                memcpy (flash, save_p, sizeof(*flash));
                save_p += sizeof(*flash);
                flash->sector = &sectors[(int)flash->sector];
                flash->thinker.function.acp1 = (actionf_p1)T_LightFlash;
                P_AddThinker (&flash->thinker);
                break;

            case tc_strobe:
                PADSAVEP();
                strobe = Z_Malloc (sizeof(*strobe), PU_LEVEL, NULL);
                memcpy (strobe, save_p, sizeof(*strobe));
                save_p += sizeof(*strobe);
                strobe->sector = &sectors[(int)strobe->sector];
                strobe->thinker.function.acp1 = (actionf_p1)T_StrobeFlash;
                P_AddThinker (&strobe->thinker);
                break;

            case tc_glow:
                PADSAVEP();
                glow = Z_Malloc (sizeof(*glow), PU_LEVEL, NULL);
                memcpy (glow, save_p, sizeof(*glow));
                save_p += sizeof(*glow);
                glow->sector = &sectors[(int)glow->sector];
                glow->thinker.function.acp1 = (actionf_p1)T_Glow;
                P_AddThinker (&glow->thinker);
                break;

            default:
                I_Error ("P_UnarchiveSpecials:Unknown tclass %i "
                         "in savegame",tclass);
        }

    }

}

